package cc.sunni.user.controller;

import cc.sunni.api.controller.user.PassportControllerApi;
import cc.sunni.common.Constant;
import cc.sunni.common.enums.UserStatus;
import cc.sunni.common.utils.*;
import cc.sunni.pojo.bo.RegisterLoginBO;
import cc.sunni.pojo.user.AppUser;
import cc.sunni.user.service.AppUserService;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.UUID;
import java.util.concurrent.ThreadLocalRandom;

/**
 * @author jl
 * @since 2021/2/20 22:06
 */
@RestController
public class PassportController implements PassportControllerApi {
    @Autowired
    private SmsUtil smsUtil;
    @Autowired
    private RedisOperator redis;
    @Autowired
    private AppUserService appUserService;
    @Value("${website.domain-name}")
    private String domainName;

    @Override
    public R getSMSCode(String mobile, HttpServletRequest request) {
        if (!RegexUtils.checkPhone(mobile)) {
            return R.en(ResultEnum.MOBILE_ERROR);
        }
        // 获得用户ip
        String userIp = IPUtil.getRequestIp(request);
        // 根据用户的ip进行限制，限制用户在60秒内只能获得一次验证码
        Boolean flag = redis.setnx60s(Constant.SMS_CODE + ":" + userIp, userIp);
        if (!flag) {
            return R.en(ResultEnum.SMS_NEED_WAIT_ERROR);
        }
        // 生成随机验证码并且发送短信
        StringBuilder sb = new StringBuilder();
        ThreadLocalRandom.current().ints(0, 9).limit(6).forEach(sb::append);
//        smsUtil.sendSms(mobile, sb.toString());
        // 把验证码存入redis，用于后续进行验证
        redis.set(Constant.SMS_CODE + ":" + mobile, sb.toString(), 1800);
        return R.ok();
    }

    @Override
    public R doLogin(@Validated RegisterLoginBO registerLoginBO, HttpServletRequest request, HttpServletResponse response) {
        String mobile = registerLoginBO.getMobile();
        String smsCode = registerLoginBO.getSmsCode();

        // 1. 校验验证码是否匹配
        String redisSMSCode = redis.get(Constant.SMS_CODE + ":" + mobile);
        if (StrUtil.isBlank(redisSMSCode) || !redisSMSCode.equalsIgnoreCase(smsCode)) {
            return R.en(ResultEnum.SMS_CODE_ERROR);
        }

        // 2. 查询数据库，判断该用户注册
        AppUser user = appUserService.getOne(new LambdaQueryWrapper<AppUser>().eq(AppUser::getMobile, mobile));
        if (user != null && user.getActiveStatus().equals(UserStatus.FROZEN.type)) {
            // 如果用户不为空，并且状态为冻结，则直接抛出异常，禁止登录
            return R.en(ResultEnum.USER_FROZEN);
        } else if (user == null) {
            // 如果用户没有注册过，则为null，需要注册信息入库
            user = appUserService.createUser(mobile);
        }

        // 3. 保存用户分布式会话的相关操作
        int userActiveStatus = user.getActiveStatus();
        if (userActiveStatus != UserStatus.FROZEN.type) {
            // 保存token到redis
            String uToken = UUID.randomUUID().toString();
            redis.set(Constant.REDIS_USER_TOKEN + ":" + user.getId(), uToken, Constant.COOKIE_MONTH);
            redis.set(Constant.REDIS_USER_INFO + ":" + user.getId(), JSONUtil.toJsonStr(user), Constant.COOKIE_MONTH);

            // 保存用户id和token到cookie中
            setCookie(response, "utoken", uToken, Constant.COOKIE_MONTH);
            setCookie(response, "uid", user.getId(), Constant.COOKIE_MONTH);
        }

        // 4. 用户登录或注册成功以后，需要删除redis中的短信验证码，验证码只能使用一次，用过后则作废
        redis.del(Constant.SMS_CODE + ":" + mobile);

        // 5. 返回用户状态
        return R.ok(userActiveStatus);
    }

    @Override
    public R logout(String userId, HttpServletRequest request, HttpServletResponse response) {
        redis.del(Constant.REDIS_USER_TOKEN + ":" + userId);
        setCookie(response, "utoken", "", Constant.COOKIE_DELETE);
        setCookie(response, "uid", "", Constant.COOKIE_DELETE);
        return R.ok();
    }

    private void setCookie(HttpServletResponse response, String cookieName, String cookieValue, Integer maxAge) {
        try {
            cookieValue = URLEncoder.encode(cookieValue, "utf-8");
            Cookie cookie = new Cookie(cookieName, cookieValue);
            cookie.setMaxAge(maxAge);
            cookie.setDomain(domainName);
            cookie.setPath("/");
            response.addCookie(cookie);
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
    }
}
